package BlobUI;

import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Vector;

import javax.media.CaptureDeviceInfo;
import javax.media.CaptureDeviceManager;
import javax.media.Format;
import javax.media.MediaLocator;
import javax.media.format.VideoFormat;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;

import UI.VideoEffectUIFactory;
import VideoProcessing.RgbVideoEffect;
import Blob.BlobPublishingCamera;
import Blob.FourNeighborBlobDetector;

public class BlobPublishingCameraPanel extends JPanel {

	/** Serialization id. */
	private static final long serialVersionUID = 1789835098650851831L;

	/** The {@link BlobPublishingCamera} that this JPanel interacts with. */
	private BlobPublishingCamera camera;

	/** Index of the effect chain element being watched. */
	private int curFeedIndex;

	// private JComboBox cmbEffect;
	private JRadioButton btnVideoInput[];

	private BlobVideoPanel pnlVideo;

	public BlobPublishingCameraPanel(BlobPublishingCamera camera) {
		if (camera == null) {
			throw new NullPointerException("camera");
		}
		if (camera.getProcessingChain() == null) {
			throw new IllegalStateException(
					"Camera must already be opened before UI may be created.");
		}

		// Register our effects' UIs
		VideoEffectUIFactory.getInstance().registerClass(
				FourNeighborBlobDetector.class,
				FourNeighborBlobDetectorPanel.class);

		//
		this.camera = camera;
		createUI();
	}

	public BlobPublishingCameraPanel(String path) {
		camera = new BlobPublishingCamera();

		if (path.equalsIgnoreCase("webcam")) {
			listDevices();
			if (autoOpen()) {
				initProcessingChain();
				this.setSize(800, 600);
				createUI();
			} else {
				JOptionPane.showMessageDialog(this, "Unable to open webcam");
			}
		} else {
			if (openVideoURL(path, "file:")) {
				initProcessingChain();
				this.setSize(800, 600);
				createUI();
			} else {
				JOptionPane.showMessageDialog(this, "Unable to open video");
			}
		}
	}

	private boolean autoOpen() {
		boolean success = false;
		int camNum = 0;
		String mediaLoc;
		do {
			mediaLoc = "vfw://" + camNum;
			System.out.println("Trying video for windows, camera #" + camNum
					+ ": " + mediaLoc);
			success = openVideoURL(mediaLoc, "");
		} while (!success && (++camNum) < 4);
		return success;
	}

	private void listDevices() {
		Vector deviceInfos = CaptureDeviceManager
				.getDeviceList(new VideoFormat("RGB"));
		if (deviceInfos.isEmpty()) {
			System.out.println("No capture devices found.");
		} else {
			for (Object o : deviceInfos) {
				CaptureDeviceInfo info = (CaptureDeviceInfo) o;
				System.out.println(info.getName() + " :");
				for (Format format : info.getFormats()) {
					System.out
							.println(String.format("\t%s", format.toString()));
				}
			}
		}
	}

	public boolean isProcessing() {
		return camera == null ? false : true;
	}

	private void initProcessingChain() {
		if (camera == null) {
			throw new NullPointerException("camera");
		}
		if (camera.getProcessingChain() == null) {
			throw new IllegalStateException(
					"Camera must already be opened before UI may be created.");
		}

		VideoEffectUIFactory.getInstance().registerClass(
				FourNeighborBlobDetector.class,
				FourNeighborBlobDetectorPanel.class);
	}

	private boolean openVideoURL(String path, String type) {
		MediaLocator ml;
		if ((ml = new MediaLocator(type + "" + path)) == null) {
			return false;
		}
		return camera.open(ml);
	}

	public void closeCameraPanel() {
		if (camera != null) {
			camera.close();
			this.switchFeed(0);
		}
	}

	private void createUI() {
		// Display the visual & control component if there's one.
		setLayout(new BorderLayout());
		// ////// Left
		JPanel pnlLeft = new JPanel();
		pnlLeft.setLayout(new BorderLayout());

		pnlVideo = new BlobVideoPanel(camera);
		pnlLeft.add("Center", pnlVideo);

		/*
		 * JCheckBox chkFlip = new JCheckBox("Flip video vertically");
		 * chkFlip.addItemListener(new ItemListener() { public void
		 * itemStateChanged(ItemEvent e) {
		 * pnlVideo.setFlipVideo(e.getStateChange() == ItemEvent.SELECTED); }
		 * }); pnlLeft.add("South", chkFlip);
		 */
		add("Center", pnlLeft);
		// ////// Right
		JPanel pnlRight = new JPanel();
		GridBagLayout gbLayout = new GridBagLayout();
		pnlRight.setLayout(gbLayout);

		ButtonGroup btngrpInput = new ButtonGroup();
		btnVideoInput = new JRadioButton[camera.getProcessingChain().length];
		JPanel pnlInput = new JPanel();
		pnlInput.setBorder(new TitledBorder(new EtchedBorder(), "Video View"));
		GridLayout inputLayout = new GridLayout(0, 1); // camera.getProcessingChain().length);
		pnlInput.setLayout(inputLayout);
		for (int i = 0; i < camera.getProcessingChain().length; i++) {
			btnVideoInput[i] = new JRadioButton(Integer.toString(i + 1) + " "
					+ camera.getProcessingChain()[i].getName());
			btngrpInput.add(btnVideoInput[i]);
			pnlInput.add(btnVideoInput[i]);

			final int index = i;
			btnVideoInput[i].addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					// if (btnVideoInput[index])
					switchFeed(index); // effectIndex);
				}
			});
		}

		GridBagConstraints constr = new GridBagConstraints();
		constr.gridwidth = 1;
		constr.gridheight = 1;
		constr.fill = GridBagConstraints.BOTH;
		constr.anchor = GridBagConstraints.NORTH;
		constr.gridwidth = GridBagConstraints.REMAINDER; // end row
		gbLayout.setConstraints(pnlInput, constr);
		pnlRight.add(pnlInput);
		// add(pnlInput, "South");

		// // Processing Chain UI
		// pnlRight.setLayout(new B)
		int i = 0;
		for (RgbVideoEffect effect : camera.getProcessingChain()) {

			// effect.createUI(pnl);
			JPanel ui = VideoEffectUIFactory.getInstance().provideFor(effect);
			if (ui != null) {
				JPanel pnl = new JPanel();
				pnl.setBorder(new TitledBorder(new EtchedBorder(), effect
						.getName()));
				pnl.add(ui);

				GridBagConstraints gbc = new GridBagConstraints();
				gbc.gridwidth = 1;
				gbc.gridheight = 1;
				gbc.fill = GridBagConstraints.BOTH;
				gbc.anchor = GridBagConstraints.NORTH;
				gbc.gridwidth = GridBagConstraints.REMAINDER; // end row
				gbLayout.setConstraints(pnl, gbc);

				pnlRight.add(pnl);
				pnl.addMouseListener(new EffectPanelMouseListener(i));
			}

			i++;
		}
		add("West", pnlRight);
	}

	public void addNotify() {
		super.addNotify();
		// pack();
		this.getParent().validate();
	}

	/*
	 * @Override public void itemStateChanged(ItemEvent e) { if(e.getSource() ==
	 * cmbEffect) { int effectIndex = Integer.parseInt(
	 * ((String)e.getItem()).substring(0, 2).trim()) - 1; if(e.getStateChange()
	 * == ItemEvent.DESELECTED) {
	 * //camera.getProcessingChain()[effectIndex].removeVideoFrameListener
	 * (this); } else if(e.getStateChange() == ItemEvent.SELECTED) {
	 * switchFeed(effectIndex); } } }
	 */

	/**
	 * Switches the video input to an effect in the camera's processing chain.
	 * 
	 * @param index
	 *            Index into the processing chain.
	 */
	public void switchFeed(int index) {
		if (camera.getBlobManager().getVideoSize().width > 0) {
			pnlVideo.setInput(camera.getProcessingChain()[index]);
			curFeedIndex = index;
		}
	}

	/**
	 * Listens to mouse events.
	 */
	private class EffectPanelMouseListener implements MouseListener {
		private int effectIndex;

		public EffectPanelMouseListener(int effectIndex) {
			this.effectIndex = effectIndex;
		}

		public void mouseClicked(MouseEvent e) {
			if (e.getClickCount() > 1) {
				switchFeed(effectIndex);
			}
		}

		public void mouseEntered(MouseEvent e) {
		}

		public void mouseExited(MouseEvent e) {
		}

		public void mousePressed(MouseEvent e) {
		}

		public void mouseReleased(MouseEvent e) {
		}
	}
}
